package com.zhny.aiot.four.solar.roll.film.v2.plugin;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.zhny.aiot.four.solar.roll.film.v2.plugin.model.AdjustableParam;
import com.zhny.aiot.four.solar.roll.film.v2.plugin.model.SolarRollFilm4GV2StoreEntry;
import com.zhny.aiot.four.solar.roll.film.v2.plugin.model.ValueParam;
import com.zhny.aiot.plugin.ICenterFacilities;
import com.zhny.aiot.plugin.IDeviceFactory;
import com.zhny.aiot.plugin.core.model.IRequestMessage;
import com.zhny.aiot.plugin.core.utils.MessageUtils;
import com.zhny.aiot.plugin.model.DeviceEntity;
import com.zhny.aiot.plugin.model.MessageEntry;
import com.zhny.aiot.plugin.model.MessageType;
import com.zhny.aiot.plugin.model.TaskEntity;
import com.zhny.aiot.plugin.zhnygateway.device.ZhnyConfig;
import com.zhny.aiot.plugin.zhnygateway.device.ZhnyDevice;
import com.zhny.aiot.plugin.zhnygateway.model.GatewayResponseMessage;
import com.zhny.aiot.plugin.zhnygateway.model.NodeRequestMessage;
import com.zhny.aiot.zhny.modbus.embedded.plugin.core.ZhnyModBusServer;
import com.zhny.aiot.zhny.modbus.embedded.plugin.core.ZhnyModbusEngine;
import com.zhny.aiot.zhny.modbus.embedded.plugin.model.ModbusCommandParam;
import io.netty.buffer.ByteBuf;
import org.pf4j.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

public class SolarRollFilm4GV2Device extends ZhnyDevice<SolarRollFilm4GV2StoreEntry> {
    private static final Logger logger=LoggerFactory.getLogger (SolarRollFilm4GV2Device.class);
    private final Map<String, ModbusCommandParam> CommandOperations;

    public SolarRollFilm4GV2Device(ZhnyModbusEngine engine, IDeviceFactory<ZhnyConfig> factory, ICenterFacilities centerFacilities, DeviceEntity entity) {
        super (engine, factory, centerFacilities, entity);

        CommandOperations=new ImmutableMap.Builder<String, ModbusCommandParam> ()
                .put ("stop", ModbusCommandParam.valueOf ((short) 6, 10, 0x0068))
                .put ("value", ModbusCommandParam.valueOf ((short) 6, 10, 0x0064))
                .put ("valuePulse", ModbusCommandParam.valueOf ((short) 6, 3, 0x0064))
                .put ("query", ModbusCommandParam.valueOf ((short) 3, 2, 3))
                .put ("queryPulse", ModbusCommandParam.valueOf ((short) 3, 3, 1))
                .put ("queryVersion", ModbusCommandParam.valueOf ((short) 3, 0, 2))
                .put ("reboot", ModbusCommandParam.valueOf ((short) 6, 10, 0xff))
                .build ();
    }

    @Override
    public void onDecoder(GatewayResponseMessage gatewayResponseMessage) {
        ByteBuf data = gatewayResponseMessage.getDeviceData ();
        MessageEntry entry=MessageUtils.createMessageEntry (this.getId ());
        entry.setType (MessageType.RESULT);
        entry.getProperties ().put ("gprs", this.getConfig ().getGprsNo ());
        entry.getProperties ().put ("address",  this.getConfig ().getAddress ());
        int address = data.readByte();
        int function = data.readByte();
        String value="";
        SolarRollFilm4GV2StoreEntry storeEntry=new SolarRollFilm4GV2StoreEntry ();
        if(function == 0x06) {
            on06StatusConvert(storeEntry,entry,data);
        }else if(function == 0x03) {
            on03StatusConvert(storeEntry,entry,data);
        }else {
            entry.setType (MessageType.ERROR);
            nofity (entry);
            return;
        }
        logger.info ("4G卷膜器：{} 实时:{}",
                this.getDeviceEntity ().getName (), storeEntry.toString());
    }
    private void on06StatusConvert(SolarRollFilm4GV2StoreEntry entry,MessageEntry msgEntry,ByteBuf buf) {
        int registerAddress = buf.readUnsignedShort ();
        if(registerAddress == 2){
            int value = buf.readUnsignedShort ();
            entry.setStatus (String.valueOf(value));
        }else if(registerAddress == 10){
            int flag = buf.readUnsignedByte ();
            int value = buf.readUnsignedByte ();
            if(flag == 0){
                if (value == 104) entry.setStatus("停止");
                else if (value == 255) entry.setManualReporting ("重启中");
                else entry.setManualReporting("运行中");
            }else {
                if(value == 101) entry.setManualReporting("触发本地上卷按钮");
                if(value == 102) entry.setManualReporting("触发本地下卷按钮");
                if(value == 103) entry.setManualReporting("触发本地学习按钮");
                if(value == 104) entry.setManualReporting("触发本地停止按钮");
                msgEntry.setType(MessageType.ALARM);
                msgEntry.setData (entry);
                nofity (msgEntry);
                return;
            }
        }else if(registerAddress == 03) {
            int value = buf.readUnsignedShort ();
            entry.setTotalNumberOfPulses (String.valueOf(value));
        }
        msgEntry.setData (entry);
        nofity (msgEntry);
        saveReal (entry);
        save (entry);
    }
    private void on03StatusConvert(SolarRollFilm4GV2StoreEntry entry,MessageEntry msgEntry,ByteBuf buf) {
        int length = buf.readUnsignedByte ();
        if(length == 6){
            int flag = buf.readUnsignedByte();
            int status = buf.readUnsignedByte();
            int totalNumberOfPulses = buf.readUnsignedShort ();
            int voltage = buf.readUnsignedShort ();
            entry.setStatus (String.valueOf(status));
            entry.setTotalNumberOfPulses (String.valueOf(totalNumberOfPulses));
            entry.setQuantity(findSOC(createVol(voltage)));
        }
        else if(length ==2){
            int totalNumberOfPulses = buf.readUnsignedShort ();
            entry.setTotalNumberOfPulses (String.valueOf(totalNumberOfPulses));
        }
        else if(length == 4){
            int flag = buf.readUnsignedByte();
            int version1 = buf.readUnsignedByte();
            int version2 = buf.readUnsignedByte();
            int version3 = buf.readUnsignedByte();
            entry.setVersion (String.format("v%s.%s.%s",version1,version2,version3));
        }
        else {
            return;
        }
        msgEntry.setData (entry);
        nofity (msgEntry);
        saveReal (entry);
        save (entry);
    }
    private String findSOC(double voltage) {
        // 定义电压和对应的SOC%
        double[] voltages = {8.766, 9.777, 10.389, 10.695, 10.926, 11.139, 11.469, 11.754, 12.033, 12.255, 12.525};
        String[] socs = {"0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"};

        // 如果电压大于最大电压值，返回100%
        if (voltage >= voltages[voltages.length - 1]) {
            return socs[socs.length - 1];
        }

        // 如果电压小于最小电压值，返回0%
        if (voltage < voltages[0]) {
            return socs[0];
        }
        return String.valueOf (calculateBatteryLevel(voltage));
    }
    private long calculateBatteryLevel(double voltage) {
        return Math.round (4 * voltage * voltage - 58.4 * voltage + 204);
    }
    private double createVol(int vol){
        double result = vol/100.00;
        return Double.parseDouble(String.format("%.2f",result));
    }

    @Override
    protected void disposable() {

    }



    @Override
    protected NodeRequestMessage onCommand(TaskEntity taskEntity) {
        MessageEntry entry=MessageUtils.createMessageEntry (this.getId ());
        entry.getProperties ().put ("task_name", taskEntity.getMethodName ());
        entry.setType (MessageType.OPERATION);
        ModbusCommandParam command=CommandOperations.get (taskEntity.getMethodName ());
        if(command == null) {
            logger.info (String.format ("factory[%s] no found func[%s]", getTypeId (), taskEntity.getMethodName ()));
            entry.setType (MessageType.ERROR);
            this.nofity (entry);
            return null;
        }
        logger.info ("quilt task func:{},param:{}",taskEntity.getMethodName (),taskEntity.getParams ());
        if(taskEntity.getMethodName ().equals ("value")){
            if(!StringUtils.isNullOrEmpty (taskEntity.getParams ())) {
                AdjustableParam param=null;
                ObjectMapper mapper = new ObjectMapper();
                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                try{
                    param = mapper.readValue (taskEntity.getParams (),AdjustableParam.class);
                }catch (Exception e){
                    logger.error ("Parse param error !");
                    return  null;
                }
                command = ModbusCommandParam.valueOf ((short)6,2, param.getValue ());
            }else {
                logger.info (String.format ("factory[%s]  set adjust value command [%s] error param value:[%s]", getTypeId (), taskEntity.getMethodName (),taskEntity.getParams ()));
                entry.setType (MessageType.ERROR);
                this.nofity (entry);
                return null;
            }
        }
        else if(taskEntity.getMethodName ().equals ("down")) ZhnyModBusServer.getInstance ().stop ();
        else if(taskEntity.getMethodName ().equals ("valuePulse")){
            if(!StringUtils.isNullOrEmpty (taskEntity.getParams ())) {
                ValueParam param=null;
                ObjectMapper mapper = new ObjectMapper();
                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                try{
                    param = mapper.readValue (taskEntity.getParams (), ValueParam.class);
                }catch (Exception e){
                    logger.error ("Parse param error !");
                    return  null;
                }
                command = ModbusCommandParam.valueOf ((short)6,3, param.getValue ());
            }else {
                logger.info (String.format ("factory[%s]  set adjust value command [%s] error param value:[%s]", getTypeId (), taskEntity.getMethodName (),taskEntity.getParams ()));
                entry.setType (MessageType.ERROR);
                this.nofity (entry);
                return null;
            }
        }
        ByteBuf data = command.enCoder((short) 1);
        byte[] modbusData = new byte[data.readableBytes()];
        data.readBytes (modbusData);
        int length = modbusData.length + 4;
        data.release();
        return new NodeRequestMessage (getzhnyType() + 128,length,0xAA,1,modbusData);
    }

    @Override
    protected int getzhnyType() {
        return 33;
    }
}
